Ajouter une recherche à votre site avec JavaScript

Les générateurs de sites Web statiques comme Gatsby et Jekyll sont populaires car ils permettent la création de pages complexes et basées sur des modèles qui peuvent être hébergées n'importe où. Mais la simplicité impressionnante des générateurs de sites Web est également limitante. La recherche est particulièrement difficile. Comment autorisez-vous les utilisateurs à effectuer des recherches lorsque vous n'avez aucune fonction serveur et aucune base de données?

Avec JavaScript!

Nous avons récemment ajouté Search au site de documentation TrackJS , construit à l'aide du générateur de site Web Jekyll et hébergé sur les pages GitHub . GitHub ne souhaitait pas trop nous laisser exécuter des fonctions de recherche sur leurs serveurs, nous avons donc dû trouver une autre façon d'exécuter la recherche en texte intégral sur notre documentation.

Notre documentation est d'environ 43 000 mots répartis sur 39 pages. Ce n'est en fait pas beaucoup de données, il s'avère que - seulement 35 kilo-octets lors de la sérialisation pour la recherche. C'est plus petit que certaines bibliothèques JavaScript.

Création de l'index de recherche

Nous avons trouvé un projet appelé Lunr.js , qui est un moteur de recherche en texte intégral léger inspiré de solr . De plus, il ne fait que 8,4 kilo-octets, nous pouvons donc facilement l'exécuter côté client.

Lunr prend un tableau d'objets à clés pour construire son index, nous devons donc fournir nos données au client sous la bonne forme. Nous pouvons sérialiser nos données pour la recherche en utilisant les filtres natifs de Jekyll comme : xml_escape, strip_html, et jsonify  

Nous les utilisons pour construire un objet avec un autre contexte de page important, comme le titre de la page et l'URL. Tout cela se rassemble sur une page  search.html

<ol id="search-results"></ol>

<script>

    window.pages = {

        {% for page in site.pages %}

            "{{ page.url | slugify }}": {

                "title": "{{ page.title | xml_escape }}",

                "content": {{ page.content | markdownify | strip_newlines | strip_html | jsonify }},

                "url": "{{ site.url | append: page.url | xml_escape }}",

                "path": "{{ page.url | xml_escape }}"

            }{% unless forloop.last %},{% endunless %}

        {% endfor %}

    };

</script>

<script src="/lunr-2.3.5.min.js"></script>

<script src="/search.js"></script>

Le fragment HTML ci-dessus est la structure de base de la page de recherche. Il crée une variable globale JavaScript ,pages ,et utilise les données Jekyll pour construire les valeurs à partir des pages de contenu du site.
Maintenant, nous devons indexer nos données de page sérialisées avec lunr. Nous traiterons notre logique de recherche personnalisée dans un script search.js séparé .

var searchIndex = lunr(function() {

    this.ref("id");

    this.field("title", { boost: 10 });

    this.field("content");

    for (var key in window.pages) {

        this.add({

            "id": key,

            "title": pages[key].title,

            "content": pages[key].content

        });

    }

});

Nous construisons notre nouveau searchIndex en racontant à lunr la forme de nos données. Nous pouvons même augmenter l'importance des champs lors de la recherche, comme augmenter l'importance des correspondances dans le titre de la page par rapport au contenu de la page. Ensuite, nous parcourons toutes nos pages globales et les ajoutons à l'index.

Maintenant, nous avons toutes nos données de page de documentation dans un moteur de recherche lunr chargé sur le client et prêt pour une recherche chaque fois que l'utilisateur visite la page /search

Lancer une recherche

Nous devons obtenir la requête de recherche de l'utilisateur pour exécuter une recherche. Je veux que l'utilisateur puisse lancer une recherche depuis n'importe où dans la documentation - pas seulement la page de recherche. Nous n'avons besoin de rien de spécial pour cela, nous pouvons utiliser un formulaire HTML à l'ancienne avec une action GET sur la page de recherche.

<form action="/search/" method="GET">

        <input required type="search" name="q" />

        <button type="submit">Search</button>

    </form>

Lorsque l'utilisateur entre sa requête de recherche, il les amènera à la page de recherche avec leur recherche dans la chaîne de requête q. Nous pouvons ramasser cela avec un peu plus de JavaScript dans notre search.js et lancer la recherche avec notre index  

function getQueryVariable(variable) {

  var query = window.location.search.substring(1);

  var vars = query.split("&");

  for (var i = 0; i < vars.length; i++) {

      var pair = vars[i].split("=");

      if (pair[0] === variable) {

          return decodeURIComponent(pair[1].replace(/\+/g, "%20"));

      }

  }

}

 

var searchTerm = getQueryVariable("q");

// creation of searchIndex from earlier example

var results = searchIndex.search(searchTerm);

var resultPages = results.map(function (match) {

  return pages[match.ref];

});

Les résultats que nous obtenons de lunr ne contiennent pas toutes les informations que nous voulons, donc nous mappons les résultats sur notre objet pages d'origine pour obtenir les informations complètes sur la page Jekyll. Maintenant, nous avons un tableau de résultats de page pour la recherche de l'utilisateur que nous pouvons afficher sur la page

Rendre les résultats

Comme pour toute autre tâche de rendu côté client, nous devons injecter nos valeurs de résultat dans un extrait HTML et les placer dans le DOM. Nous n'utilisons aucun framework de rendu JavaScript sur le site de documentation de TrackJS, nous le ferons donc avec du JavaScript ancien

// resultPages from previous example

resultsString = "";

resultPages.forEach(function (r) {

    resultsString += "<li>";

    resultsString +=   "<a class='result' href='" + r.url + "?q=" + searchTerm + "'><h3>" + r.title + "</h3></a>";

    resultsString +=   "<div class='snippet'>" + r.content.substring(0, 200) + "</div>";

    resultsString += "</li>"

});

document.querySelector("#search-results").innerHTML = resultsString;

ISi vous souhaitez mettre d'autres propriétés de page dans les résultats, comme des balises, vous devez les ajouter à votre sérialiseur pour les avoir dans resultsPages.

Vous pouvez le voir en action et extraire le code final poli sur la page de documentation TrackJS . Bien sûr, avec tout ce JavaScript, vous devrez le surveiller pour les bugs. TrackJS peut vous aider avec cela, obtenez votre essai gratuit du meilleur service de surveillance des erreurs disponible aujourd'hui et assurez-vous que votre JavaScript continue de fonctionner correctement.

Prêt pour une recherche encore meilleure? Découvrez "Site Search with JavaScript Part 2", sur le blog TrackJS . Nous développons cet exemple et améliorons les extraits de résultats de recherche pour afficher un meilleur contexte du terme de recherche et une mise en évidence dynamique du terme de recherche dans les pages. Cela améliore vraiment l'expérience utilisateur.